#include "lv_barcode.h"
#ifdef USE_LV_BARCODE

#include <stdio.h>
#include <string.h>
#include "code128.h"
#include "../lv_widgets/lv_img.h"
#include "../lv_draw/lv_draw.h"
#include "lv_drivers/display/scaler.h"
#include "lvgl/src/lv_draw/lv_img_cache.h"

static lv_signal_cb_t ancestor_signal;

static lv_res_t barcode_signal(lv_obj_t * img, lv_signal_t sign, void * param)
{
    lv_res_t res;

    /* Include the ancient signal function */
    res = ancestor_signal(img, sign, param);
    if(res != LV_RES_OK) return res;

    if(sign == LV_SIGNAL_CLEANUP) {
        lv_img_dsc_t *img_des = (lv_img_dsc_t *)lv_img_get_src(img);
        if(img_des != NULL) {
            lv_img_cache_invalidate_src(img_des);
            lv_mem_aligned_free((void *)img_des->data);
            lv_mem_free(img_des);
        }
    }

    return res;
}

lv_obj_t *barcode_create(lv_obj_t * par, uint16_t width, uint16_t height, const char *bc_buf)
{
    lv_obj_t *obj = NULL;
    if(par == NULL || bc_buf == NULL || width == 0 || height == 0) {
        printf("barcode_create, param error!!\n");
        return NULL;
    }

    size_t size = code128_estimate_len(bc_buf);
    uint8_t *barcode_data = lv_mem_alloc(size);
    if(barcode_data == NULL) {
        printf("barcode_create, no memory 0!!\n");
        return NULL;
    }
    size = code128_encode_gs1(bc_buf, (char *)barcode_data, size);

    lv_color_t *orig_color_map = lv_mem_aligned_alloc(size*height*2, 32);
    if(orig_color_map == NULL) {
        printf("barcode_create, no memory 1!!\n");
        lv_mem_free(barcode_data);
        return NULL;
    }
    memset(orig_color_map, 0xff, size*height*2);

    int i = 0, j = 0;
    for(i=0; i<size; i++) {
        if(barcode_data[i]) {
            for(j=0; j<height; j++) {
                memset(&orig_color_map[j*size + i], 0x0, sizeof(lv_color_t));
            }
        }
    }

    lv_color_t *dst_color_map = lv_mem_aligned_alloc(width*height*2, 32);
    if(dst_color_map == NULL) {
        printf("barcode_create, no memory 2!!\n");
        goto end;
    }

    if(rgb565_scale(size, height, width, height, (uint8_t *)orig_color_map, (uint8_t *)dst_color_map) == 0) {
        lv_mem_aligned_free(dst_color_map);
        goto end;
    }

    lv_img_dsc_t *img_des = lv_mem_alloc(sizeof(lv_img_dsc_t));
    if(img_des == NULL) {
        printf("barcode_create, no memory 3!!\n");
        lv_mem_aligned_free(dst_color_map);
        goto end;
    }
    lv_img_cache_invalidate_src(img_des);
    memset(img_des, 0 , sizeof(lv_img_dsc_t));
    img_des->header.w = width;
    img_des->header.h = height;
    img_des->header.cf = LV_IMG_CF_TRUE_COLOR;
    img_des->data_size = width*height*2;
    img_des->data = (const uint8_t *)dst_color_map;

    obj = lv_img_create(par, NULL);
    lv_img_set_src(obj, img_des);

    if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(obj);
    /*The signal and design functions are not copied so set them here*/
    lv_obj_set_signal_cb(obj, barcode_signal);

end:
    lv_mem_aligned_free(orig_color_map);
    lv_mem_free(barcode_data);
    return obj;
}

#endif
